package ex205.exp;

import java.util.Stack;

public class ArithmeticExpression {
    // 表示运算符的正则表达式
    private final static String ALL_OPER = "[\\+\\-\\*/]";
    private final static String ADD_OR_SUB = "[\\+\\-]";
    private final static String MUL_OR_DIV = "[\\*/]";
    private final static String LEFT_BRACKET = "[\\(]";
    private final static String RIGHT_BRACKET = "[\\)]";

    private String expression;             // 表达式字符串
    private Stack<Integer> operandStack;   // 运算符栈
    private Stack<String> operatorStack;   // 运算数栈

    /**
     * 根据给定的字符串构建一个表达式对象
     *
     * @param expression 表达式字符串
     */
    public ArithmeticExpression(String expression) {
        this.expression = expression;
        operandStack = new Stack<>();
        operatorStack = new Stack<>();
    }

    public int getResult() {
        String[] symbols = getRegularExpression().split(" ");
        for (String symbol : symbols) {
            // symbol 存放从表达式中刚刚取出的符号，可能是运算符、运算数或括号
            if (symbol.matches(ADD_OR_SUB)) { // + 或 -
                // 处理已经入栈的优先级大于或等于symbol的运算符
                while (!operatorStack.isEmpty() && operatorStack.peek().matches(ALL_OPER)) {
                    processOneOperator();
                }
                operatorStack.push(symbol); // 当前运算符入栈
            } else if (symbol.matches(MUL_OR_DIV)) { // * 或 /
                // 处理已经入栈的优先级大于或等于symbol的运算符
                while (!operatorStack.isEmpty() && operatorStack.peek().matches(MUL_OR_DIV)) {
                    processOneOperator();
                }
                operatorStack.push(symbol); // 当前运算符入栈
            } else if (symbol.matches(LEFT_BRACKET)) { // (
                operatorStack.push(symbol); // 当前运算符入栈
            } else if (symbol.matches(RIGHT_BRACKET)) { // )
                while (!operatorStack.peek().matches(LEFT_BRACKET)) {
                    processOneOperator();
                }
                operatorStack.pop(); // 弹出栈顶的(
            } else {  // 运算数
                operandStack.push(Integer.valueOf(symbol));
            }
        }

        while (!operatorStack.isEmpty()) {
            processOneOperator();
        }

        return operandStack.pop(); // 返回运算数栈顶数据
    }

    /**
     * 对表达式字符串进行预处理，生成符合题目要求的字符串
     *
     * @return 规范的表达式字符串
     */

    public String getRegularExpression() {
        String result = null;

        result = expression.replaceAll("[\\s]+", ""); // 去除所有空白字符
        result = result.replaceAll("\\*", " * ");     // * 两边各加1个空格
        result = result.replaceAll("\\+", " + ");     // + 两边各加1个空格
        result = result.replaceAll("\\-", " - ");     // - 两边各加1个空格
        result = result.replaceAll("/", " / ");       // / 两边各加1个空格
        result = result.replaceAll("\\(", "( ");      // ( 右边加1个空格
        result = result.replaceAll("\\)", " )");      // ) 左边加1个空格

        return result;
    }

    /**
     * 处理栈顶的一个运算符
     */
    private void processOneOperator() {
        String operator = operatorStack.pop(); // 弹出栈顶运算符
        int operand1 = operandStack.pop();     // 弹出栈顶运算数
        int operand2 = operandStack.pop();     // 弹出栈顶运算数

        /* JDK14+
        int result = switch (operator) {
            case "+" -> operand2 + operand1;
            case "-" -> operand2 - operand1;
            case "*" -> operand2 * operand1;
            case "/" -> operand2 / operand1;
            default -> 0;
        };
        */

        int result = 0;
        switch (operator) {
            case "+":
                result = operand2 + operand1;
                break;
            case "-":
                result = operand2 - operand1;
                break;
            case "*":
                result = operand2 * operand1;
                break;
            case "/":
                result = operand2 / operand1;
                break;
            default:
                result = 0;
        }

        operandStack.push(result); // 运算结果入运算数栈
    }

    // setters && getters
    public String getExpression() {
        return expression;
    }

    public void setExpression(String expression) {
        this.expression = expression;
    }
}
